<template>
  <div class="upload-container">
    <div class="trigger-container" @click="onUpload">
      <input
        class="hidden"
        ref="fileUploader"
        type="file"
        :multiple="multiple"
        :accept="acceptType"
        @change="fileChange"
      />
      <slot></slot>
    </div>
    <div v-if="help" class="file-help">
      {{help}}
    </div>
    <ul class="files-container" v-if="showFiles">
      <li v-for="file in fileList" :key="file.name" class="sspace-vertical">
        <s-icon icon="icon-file" type="symbol"/>
        <span class="sspace-horizon">{{file.name}}</span>
      </li>
    </ul>
  </div>
</template>

<script lang='ts'>
import $axios from "@/request"
import { file } from "@babel/types"
import { PropType, computed, ref, watch, reactive, toRefs } from "vue"
import { Message, SIcon } from "@/components"

type Limit={
    size?:number, // 文件大小  单位M
    maxFiles?:number, // 文件数量
    [index:string]:string|number|undefined
}
enum FILE_STATUS{
    EMPTY=0,
    SUCCESS=1,
    ERROR=2,
    UPLOADING=3
}
// 状态的结构
type State={
    fileData:any[]|object,
    fileStatus:FILE_STATUS,
    fileList:FileList|[],
    fileIndex:number
}
export default {
  name: "s-upload",
  components: { SIcon },
  props: {
    action: String,
    initFile: {
      type: [Array, Object],
      default: null
    },
    accept: {
      type: [String, Array],
      default: "image/*"
    },
    limit: Object as PropType<Limit>,
    multiple: {
      type: Boolean,
      default: false
    },
    beforeUpload: Function as PropType<(files:FileList)=>boolean>,
    showFiles: {
      type: Boolean,
      default: false
    },
    help: String
  },

  emits: ["onSuccess", "onError"],

  setup (props, context) {
    const fileUploader = ref<null | HTMLInputElement>(null)

    const acceptType = computed(() => {
      if (typeof props.accept !== "string") {
        if (Array.isArray(props.accept)) {
          return props.accept.join()
        } else {
          console.error("accept接收字符串或数组，请输入正确的格式")
        }
      }
      return props.accept
    })

    const state = reactive<State>({
      fileData: props.initFile,
      fileStatus: FILE_STATUS.ERROR,
      fileList: [],
      fileIndex: 0
    })

    // 监听是否有初始文件
    watch(() => props.initFile, (val) => {
      if (val) {
        state.fileStatus = FILE_STATUS.SUCCESS
        state.fileData = val
      }
    })

    const onUpload = (e:Event) => {
      if (fileUploader.value) {
        // 判断状态决定是否上传
        fileUploader.value.click()
      }
    }

    // 自定义验证
    const customCheck = async (files:FileList) => {
      return new Promise((resolve, reject) => {
        if (props.beforeUpload) {
          const result = props.beforeUpload(files)
          if (typeof result !== "boolean") {
            reject(new Error("beforeUploadu应该返回一个布尔值"))
          }
          resolve(result)
        } else {
          resolve(true)
        }
      })
    }

    // 文件大小验证
    const sizeCheck = (files:FileList) => {
      return new Promise((resolve, reject) => {
        const { size } = props.limit
        if (size) {
          let index = 0
          while (index < files.length) {
            const file = files[index]
            const fileSize = file.size / 1024
            if (fileSize > size) {
              const msg = `${file.name}文件大小超出${size}K，请重新调整！`
              Message.error(msg)
              reject(new Error(msg))
            }
            index++
          }
          resolve(true)
        }
        resolve(true)
      })
    }

    // 文件数量验证
    const lengthCheck = (files:FileList) => {
      return new Promise((resolve, reject) => {
        const { maxFiles } = props.limit
        if (maxFiles) {
          console.log(files.length, maxFiles)
          if (files.length > maxFiles) {
            const msg = `文件数量不得超过${maxFiles}个`
            Message.error(msg)
            reject(new Error(msg))
          }
          resolve(true)
        }
        resolve(true)
      })
    }

    const fileChange = async (e:Event) => {
      const target = e.target as HTMLInputElement
      const files = target.files
      if (files && file.length) {
        // 上传前验证
        await customCheck(files)
        if (props.limit) {
          await sizeCheck(files)
          await lengthCheck(files)
        }

        // 本地 不上传到服务器时
        if (!props.action) {
          context.emit("onSuccess", files)
          state.fileList = files
          state.fileStatus = FILE_STATUS.SUCCESS
        } else {
          state.fileStatus = FILE_STATUS.UPLOADING
          state.fileList = files
          uploadFile(files[state.fileIndex])
        }
      }
    }

    // 上传文件
    const uploadFile = async (file:File) => {
      try {
        const fd = new FormData()
        fd.append("file", file)
        // const data = await $axios.upload(props.action, fd)
        //   if (true) {
        //   await isFinish()
        // } else {
        //   throw new Error(`${file.name}在上传过程中发生错误，上传中止`)
        // }
      } catch (err) {
        state.fileStatus = FILE_STATUS.ERROR
        state.fileList = []
        context.emit("onError")
        console.error(err)
      } finally {
        state.fileIndex = 0
      }
      state.fileStatus = FILE_STATUS.SUCCESS
      context.emit("onSuccess", state.fileList)
      state.fileList = []
    }

    // 遍历文件
    const isFinish = () => {
      return new Promise((resolve, reject) => {
        // 如果有多个文件
        if (props.multiple && state.fileList.length > 1) {
          // 判断当前文件索引和文件列表长度
          if (state.fileIndex < state.fileList.length - 1) {
            state.fileIndex++
            uploadFile(state.fileList[state.fileIndex])
          } else {
            resolve(FILE_STATUS.SUCCESS)
          }
        } else {
          resolve(FILE_STATUS.SUCCESS)
        }
      })
    }

    return {
      acceptType,
      fileChange,
      onUpload,
      fileUploader,
      ...toRefs(state),
      ...toRefs(props)
    }
  }
}
</script>

<style scoped lang="less">
.upload-container{
  display: flex;
  flex-direction: column;
  .file-help{
    color:@help;
    margin: 10px 0;
    font-size: 0.85em;
  }
  .files-container{
    cursor: default;
  }
}

</style>
